﻿namespace Fiddler
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Runtime.CompilerServices;
    using System.Text;

    internal class HTTPSServerHello
    {
        private List<string> _Extensions;
        private int _HandshakeVersion;
        private uint _iCipherSuite;
        private int _iCompression;
        private int _MajorVersion;
        private int _MessageLen;
        private int _MinorVersion;
        private byte[] _Random;
        private byte[] _SessionID;
        [CompilerGenerated]
        private bool _bNPNToSPDY;

        private static string ExtensionListToString(List<string> slExts)
        {
            if ((slExts != null) && (slExts.Count >= 1))
            {
                return string.Join("\n\t", slExts.ToArray());
            }
            return "\tnone";
        }

        private static string GetNPNString(byte[] arrData)
        {
            int num2;
            StringBuilder builder = new StringBuilder();
            for (int i = 0; i < arrData.Length; i += 1 + num2)
            {
                num2 = arrData[i];
                builder.AppendFormat("{0}; ", Encoding.ASCII.GetString(arrData, i + 1, num2));
            }
            return builder.ToString();
        }

        internal bool LoadFromStream(Stream oNS)
        {
            int num = 0;
            int num2 = oNS.ReadByte();
            switch (num2)
            {
                case 0x16:
                {
                    this._HandshakeVersion = 3;
                    this._MajorVersion = oNS.ReadByte();
                    this._MinorVersion = oNS.ReadByte();
                    int num3 = oNS.ReadByte() << 8;
                    num3 += oNS.ReadByte();
                    oNS.ReadByte();
                    byte[] buffer = new byte[3];
                    num = oNS.Read(buffer, 0, buffer.Length);
                    this._MessageLen = ((buffer[0] << 0x10) + (buffer[1] << 8)) + buffer[2];
                    this._MajorVersion = oNS.ReadByte();
                    this._MinorVersion = oNS.ReadByte();
                    this._Random = new byte[0x20];
                    num = oNS.Read(this._Random, 0, 0x20);
                    int num4 = oNS.ReadByte();
                    this._SessionID = new byte[num4];
                    num = oNS.Read(this._SessionID, 0, this._SessionID.Length);
                    this._iCipherSuite = (uint) ((oNS.ReadByte() << 8) + oNS.ReadByte());
                    this._iCompression = oNS.ReadByte();
                    if ((this._MajorVersion >= 3) && ((this._MajorVersion != 3) || (this._MinorVersion >= 1)))
                    {
                        buffer = new byte[2];
                        if (oNS.Read(buffer, 0, buffer.Length) >= 2)
                        {
                            int num5 = (buffer[0] << 8) + buffer[1];
                            if (num5 < 1)
                            {
                                return true;
                            }
                            buffer = new byte[num5];
                            if (oNS.Read(buffer, 0, buffer.Length) == buffer.Length)
                            {
                                this.ParseServerHelloExtensions(buffer);
                            }
                        }
                    }
                    return true;
                }
                case 0x15:
                {
                    byte[] buffer2 = new byte[7];
                    oNS.Read(buffer2, 0, 7);
                    FiddlerApplication.Log.LogFormat("Got an alert from the server!\n{0}", new object[] { Utilities.ByteArrayToHexView(buffer2, 8) });
                    return false;
                }
            }
            this._HandshakeVersion = 2;
            oNS.ReadByte();
            if (0x80 != (num2 & 0x80))
            {
                oNS.ReadByte();
            }
            if (oNS.ReadByte() != 4)
            {
                return false;
            }
            this._SessionID = new byte[1];
            oNS.Read(this._SessionID, 0, 1);
            oNS.ReadByte();
            this._MinorVersion = oNS.ReadByte();
            this._MajorVersion = oNS.ReadByte();
            return true;
        }

        private void ParseServerHelloExtension(int iExtType, byte[] arrData)
        {
            if (this._Extensions == null)
            {
                this._Extensions = new List<string>();
            }
            switch (iExtType)
            {
                case 0x3374:
                {
                    string nPNString = GetNPNString(arrData);
                    this._Extensions.Add(string.Format("\tNextProtocolNegotiation\t{0}", nPNString));
                    if (nPNString.Contains("spdy/"))
                    {
                        this.bNPNToSPDY = true;
                    }
                    return;
                }
                case 0x754f:
                    this._Extensions.Add(string.Format("\tchannel_id(GoogleDraft)\t{0}", Utilities.ByteArrayToString(arrData)));
                    return;

                case 0xff01:
                    this._Extensions.Add(string.Format("\trenegotiation_info\t{0}", Utilities.ByteArrayToString(arrData)));
                    return;

                case 0:
                    this._Extensions.Add(string.Format("\tserver_name\t{0}", Utilities.ByteArrayToString(arrData)));
                    return;

                case 1:
                    this._Extensions.Add(string.Format("\tmax_fragment_length\t{0}", Utilities.ByteArrayToString(arrData)));
                    return;

                case 2:
                    this._Extensions.Add(string.Format("\tclient_certificate_url\t{0}", Utilities.ByteArrayToString(arrData)));
                    return;

                case 3:
                    this._Extensions.Add(string.Format("\ttrusted_ca_keys\t{0}", Utilities.ByteArrayToString(arrData)));
                    return;

                case 4:
                    this._Extensions.Add(string.Format("\ttruncated_hmac\t{0}", Utilities.ByteArrayToString(arrData)));
                    return;

                case 5:
                    this._Extensions.Add(string.Format("\tstatus_request\t{0}", Utilities.ByteArrayToString(arrData)));
                    return;

                case 6:
                    this._Extensions.Add(string.Format("\tuser_mapping\t{0}", Utilities.ByteArrayToString(arrData)));
                    return;

                case 9:
                    this._Extensions.Add(string.Format("\tcert_type\t{0}", Utilities.ByteArrayToString(arrData)));
                    return;

                case 10:
                    this._Extensions.Add(string.Format("\telliptic_curves\t{0}", Utilities.ByteArrayToString(arrData)));
                    return;

                case 11:
                    this._Extensions.Add(string.Format("\tec_point_formats\t{0}", Utilities.ByteArrayToString(arrData)));
                    return;

                case 12:
                    this._Extensions.Add(string.Format("\tsrp_rfc_5054\t{0}", Utilities.ByteArrayToString(arrData)));
                    return;

                case 13:
                    this._Extensions.Add(string.Format("\tsignature_algorithms\t{0}", Utilities.ByteArrayToString(arrData)));
                    return;

                case 14:
                    this._Extensions.Add(string.Format("\tuse_srtp\t{0}", Utilities.ByteArrayToString(arrData)));
                    return;

                case 0x23:
                    this._Extensions.Add(string.Format("\tSessionTicket TLS\t{0}", Utilities.ByteArrayToString(arrData)));
                    return;
            }
            this._Extensions.Add(string.Format("\t0x{0:x4}\t\t{1}", iExtType, Utilities.ByteArrayToString(arrData)));
        }

        private void ParseServerHelloExtensions(byte[] arrExtensionsData)
        {
            int index = 0;
            try
            {
                while (index < arrExtensionsData.Length)
                {
                    int iExtType = (arrExtensionsData[index] << 8) + arrExtensionsData[index + 1];
                    int num3 = (arrExtensionsData[index + 2] << 8) + arrExtensionsData[index + 3];
                    byte[] dst = new byte[num3];
                    Buffer.BlockCopy(arrExtensionsData, index + 4, dst, 0, dst.Length);
                    try
                    {
                        this.ParseServerHelloExtension(iExtType, dst);
                    }
                    catch (Exception exception)
                    {
                        FiddlerApplication.Log.LogFormat("Error parsing server TLS extension. {0}", new object[] { Utilities.DescribeException(exception) });
                    }
                    index += 4 + num3;
                }
            }
            catch (Exception exception2)
            {
                FiddlerApplication.Log.LogFormat("Error parsing server TLS extensions. {0}", new object[] { Utilities.DescribeException(exception2) });
            }
        }

        public override string ToString()
        {
            StringBuilder builder = new StringBuilder(0x200);
            if (this._HandshakeVersion == 2)
            {
                builder.Append("A SSLv2-compatible ServerHello handshake was found. In v2, the ~client~ selects the active cipher after the ServerHello, when sending the Client-Master-Key message. Fiddler only parses the handshake.\n\n");
            }
            else
            {
                builder.Append("A SSLv3-compatible ServerHello handshake was found. Fiddler extracted the parameters below.\n\n");
            }
            builder.AppendFormat("Major Version:\t{0}\n", this._MajorVersion);
            builder.AppendFormat("Minor Version:\t{0}\n", this._MinorVersion);
            builder.AppendFormat("SessionID:\t{0}\n", Utilities.ByteArrayToString(this._SessionID));
            if (this._HandshakeVersion == 3)
            {
                builder.AppendFormat("Random:\t\t{0}\n", Utilities.ByteArrayToString(this._Random));
                builder.AppendFormat("Cipher:\t\t{0} [0x{1:X4}]\n", this.CipherSuite, this._iCipherSuite);
            }
            builder.AppendFormat("CompressionSuite:\t{0} [0x{1:X2}]\n", this.CompressionSuite, this._iCompression);
            builder.AppendFormat("Extensions:\n\t{0}\n", ExtensionListToString(this._Extensions));
            return builder.ToString();
        }

        public bool bNPNToSPDY
        {
            [CompilerGenerated]
            get
            {
                return this._bNPNToSPDY;
            }
            [CompilerGenerated]
            set
            {
                this._bNPNToSPDY = value;
            }
        }

        private string CipherSuite
        {
            get
            {
                if (this._iCipherSuite < HTTPSClientHello.SSL3CipherSuites.Length)
                {
                    return HTTPSClientHello.SSL3CipherSuites[this._iCipherSuite];
                }
                if (HTTPSClientHello.dictTLSCipherSuites.ContainsKey(this._iCipherSuite))
                {
                    return HTTPSClientHello.dictTLSCipherSuites[this._iCipherSuite];
                }
                return "Unrecognized cipher - See http://www.iana.org/assignments/tls-parameters/";
            }
        }

        private string CompressionSuite
        {
            get
            {
                if (this._iCompression < HTTPSClientHello.HTTPSCompressionSuites.Length)
                {
                    return HTTPSClientHello.HTTPSCompressionSuites[this._iCompression];
                }
                return "Unrecognized compression format";
            }
        }

        public string SessionID
        {
            get
            {
                if (this._SessionID == null)
                {
                    return string.Empty;
                }
                return Utilities.ByteArrayToString(this._SessionID);
            }
        }
    }
}

